﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using GongSolutions.Wpf.DragDrop;
using Socium.Domain;
using Socium.Domain.Collections;
using Socium.Presentation;

namespace Socium.Persons {
	class PeopleViewModel : PageViewModel, IDropTarget {
		public PeopleViewModel(string display_name, Database database, EnvironmentService environment_service) : base(display_name) {
			_groups.Add(new GroupTreeItem("People", database.Groups, database.Persons));

			EnvironmentService = environment_service;
		}

		#region Groups

		readonly ICollection<GroupTreeItem> _groups = new ObservableCollection<GroupTreeItem>();

		public ICollection<GroupTreeItem> Groups {
			get { return _groups; }
		}

		#endregion

		#region SelectedGroup

		GroupTreeItem _selected_group;

		public GroupTreeItem SelectedGroup {
			get { return _selected_group; }
			set {
				_selected_group = value;

				OnPropertyChanged("SelectedGroup");
			}
		}

		#endregion

		#region DragDrop

		public void DragOver(IDropInfo drop_info) {
			var effects = DragDropEffects.None;

			var target = drop_info.TargetItem as GroupTreeItem;
			if (target != null) {
				var person = drop_info.Data as PersonLineItem;
				if (person != null) {
					effects = GetEffects(person, target);
				}
				else {
					var group = drop_info.Data as GroupTreeItem;
					if (group != null) {
						effects = GetEffects(group, target);
					}
					else {
						var persons = drop_info.Data as IEnumerable<PersonLineItem>;
						if (persons != null) {
							effects = GetEffects(persons, target);
						}
					}
				}
			}

			drop_info.Effects = effects;
		}

		

		public void Drop(IDropInfo drop_info) {
			var target = drop_info.TargetItem as GroupTreeItem;
			if (target != null) {
				var person = drop_info.Data as PersonLineItem;
				if (person != null) {
					Drop(person, target);
				}
				else {
					var group = drop_info.Data as GroupTreeItem;
					if (group != null) {
						Drop(group, target);
					}
					else {
						var persons = drop_info.Data as IEnumerable<PersonLineItem>;
						if (persons != null) {
							Drop(persons, target);
						}
					}
				}
			}
		}
		

		DragDropEffects GetEffects(PersonLineItem person_line_item, GroupTreeItem target) {
			const DragDropEffects none = DragDropEffects.None;

			if (target.Parent == null) {
				return none;
			}

			var selected_group = SelectedGroup;
			if (target == selected_group) {
				return none;
			}

			if (target.Contains(person_line_item)) {
				return none;
			}

			if (selected_group.Parent == null) {
				return DragDropEffects.Copy;
			}

			return DragDropEffects.Move;
		}

		void Drop(PersonLineItem person_line_item, GroupTreeItem target) {
			var effects = GetEffects(person_line_item, target);
			if (effects == DragDropEffects.None) {
				return;
			}

			if (effects == DragDropEffects.Move) {
				var current_group = SelectedGroup;
				current_group.Remove(person_line_item);
			}

			if (effects == DragDropEffects.Copy || effects == DragDropEffects.Move) {
				target.Add(person_line_item);
			}
			else {
				throw new InvalidOperationException();
			}
		}

		DragDropEffects GetEffects(IEnumerable<PersonLineItem> persons, GroupTreeItem target) {
			return persons.Reduce(
				p => GetEffects(p, target),
				(p, acc) => {
					var e = GetEffects(p, target);
					if (e != acc || acc == DragDropEffects.None) {
						return DragDropEffects.None;
					}

					return e;
				}
			);
		}

		void Drop(IEnumerable<PersonLineItem> items, GroupTreeItem target) {
			var array = items.ToArray();
			var effects = GetEffects(array, target);
			if(effects != DragDropEffects.None) {
				array.Iterate(item => Drop(item, target));
			}
		}

		static DragDropEffects GetEffects(GroupTreeItem group_tree_item, GroupTreeItem target) {
			const DragDropEffects none = DragDropEffects.None;

			if (target == group_tree_item) {
				return none;
			}

			if (group_tree_item.Parent == null) {
				return none;
			}

			if (target.Parent == group_tree_item) {
				return none;
			}

			return DragDropEffects.Move;
		}

		static void Drop(GroupTreeItem group_tree_item, GroupTreeItem target) {
			var effects = GetEffects(group_tree_item, target);
			if (effects == DragDropEffects.None) {
				return;
			}

			if (effects == DragDropEffects.Move) {
				group_tree_item.Parent = target;
			}
			else {
				throw new InvalidOperationException();
			}
		}

		#endregion

		#region EnvironmentService

		public EnvironmentService EnvironmentService { get; private set; }

		#endregion
	}
}
